import pygame, sys
from pygame.locals import *
import math

#调速度
FPS = 20 # frames per second setting

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED   = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE  = (0, 0, 255)
PINK  = (255, 0, 255)
GRAY  = pygame.Color('Gray')

# 网格的左上角坐标
orgx = 100
orgy = 100

offx = 180
offy = 200

# 坐标原点
axis_x = orgx + offx
axis_y = orgy + offy

start = 0
''' 公式: y = A * sin(w*x + f) + b '''

''' 周期 '''
w = 1.0
T = 360 / w
A = 100

''' 计算周期函数里每个x对应的sin值 '''
def MakeSinArr(w, T):
    sinArr = {}
    for degree in range(0, int(T)+1):
        ''' 角度转换为弧度值 '''
        rad = math.radians(degree)
        ''' 计算 sin(rad) 的值'''
        y = math.sin(rad * w)
        ''' 添加到字典里 '''
        sinArr[degree] = y
    return sinArr

''' 画 x 方向的 sin 曲线 '''
def DawSinCurveX(sinArr, offset, color):
    global axis_x, axis_y
    drawCircle(sinArr, start)
    for x in range(int(0), int(T), 1):
        y = axis_y - A * sinArr[int((x+offset)%T)]
        pygame.draw.circle(DISPLAYSURF, color, (x+axis_x, y), 1)

''' 画 y 轴 sin 曲线 '''
def DawSinCurveY(sinArr, offset, color):
    global axis_x, axis_y
    drawCircle(sinArr, start)
    for y in range(int(0), 201, 1):
        x = axis_x - A * sinArr[int((y+offset)%T)]
        pygame.draw.circle(DISPLAYSURF, color, (x, axis_y-y), 1)

'''  初始化配置 '''
def initEvn():
    global start, axis_x, axis_y
    start = 0
    axis_y = orgy + offy
    axis_x = orgx + offx
    
def drawBg():
    blockWidth = 90
    blockHeight = 100
    gridWidth = 90 * 7
    gridHeight = 100 * 4
    
    ''' 填充背景 白色 '''
    DISPLAYSURF.fill(pygame.Color("WHITE"))

    ''' 画竖线 '''
    for x in range(orgx, gridWidth + orgx + 1, 10):
        if not (x-orgx) % blockWidth:
            pygame.draw.line(DISPLAYSURF, BLACK, (x, orgy), (x, orgy + gridHeight), 1)
        else:
            pygame.draw.line(DISPLAYSURF, GRAY, (x, orgy), (x, orgy + gridHeight), 1)

    ''' 画横线 '''
    for y in range(orgy, gridHeight + orgy + 1, 10):
        if not (y-orgy) % blockHeight:
            pygame.draw.line(DISPLAYSURF, BLACK, (orgx, y), (orgx + gridWidth, y), 1)
        else:
            pygame.draw.line(DISPLAYSURF, GRAY, (orgx, y), (orgx + gridWidth, y), 1)

    ''' 画横纵轴 '''
    xStart = (orgx-20, orgy+200)
    xEnd = (orgx + gridWidth +20, orgy+200)

    yStart = (orgx + offx, orgy-20)
    yEnd = (orgx + offx, orgy + gridHeight +20)
    
    pygame.draw.line(DISPLAYSURF, BLACK, xStart, xEnd, 2)
    pygame.draw.line(DISPLAYSURF, BLACK, yStart, yEnd, 2)

    ''' 画横纵轴箭头 '''
    pygame.draw.line(DISPLAYSURF, BLACK, (orgx + gridWidth + 20 - 5, orgy+200-5), xEnd, 2)
    pygame.draw.line(DISPLAYSURF, BLACK, (orgx + gridWidth + 20 - 5 , orgy+200+5), xEnd, 2)

    pygame.draw.line(DISPLAYSURF, BLACK, (orgx+offx-5, orgy-15), yStart, 2)
    pygame.draw.line(DISPLAYSURF, BLACK, (orgx+offx+5, orgy-15), yStart, 2)

    ''' 画坐标单位值 '''
    for y in range(0, gridHeight+1, 100):
        yUnitSurf = BASICFONT.render('%d'%(200-y), True, BLACK)
        yUnitRect = yUnitSurf.get_rect()
        yUnitRect.center = (orgx - 30, orgy + y)
        DISPLAYSURF.blit(yUnitSurf, yUnitRect)
    for x in range(0, gridWidth+1, 90):
        xUnitSurf = BASICFONT.render('%d'%(x-axis_x), True, BLACK)
        xUnitRect = xUnitSurf.get_rect()
        xUnitRect.center = (orgx + x, orgy - 30)
        DISPLAYSURF.blit(xUnitSurf, xUnitRect)
    
'''  画函数信息， 振幅A 周期T 角速度w '''
def drawInfo(A,w,T):
    global FPS, start
    infoSurf = BASICFONT.render(' A: %5d  w: %5.2f  T: %8.2f  FPS: %3d' % (abs(A), w, T, FPS), True, BLACK)
    infoRect = infoSurf.get_rect()
    infoRect.topleft = (200, 20)

    sinSurf = BASICFONT.render('sin curve : %3d'%(start%T), True, RED)
    sinRect = sinSurf.get_rect()
    sinRect.topleft = (orgx, 530)

    cosSurf = BASICFONT.render('cos curve : %3d'%(start%T), True, GREEN)
    cosRect = cosSurf.get_rect()
    cosRect.topleft = (orgx+200, 530)
    
    DISPLAYSURF.blit(sinSurf, sinRect)
    DISPLAYSURF.blit(cosSurf, cosRect)
    DISPLAYSURF.blit(infoSurf, infoRect)

def drawCircle(sinArr, start):
    global axis_y
    pygame.draw.circle(DISPLAYSURF, pygame.Color('Orange'), (axis_x, axis_y), A, 2)
    
    y = axis_y - A * sinArr[int((start)%T)]
    x_off = A * sinArr[int((start+90)%T)]
    x = axis_x + x_off

    pygame.draw.line(DISPLAYSURF, RED, (axis_x, axis_y), (axis_x, y), 3)
    pygame.draw.line(DISPLAYSURF, BLUE, (axis_x-x_off, axis_y), (axis_x, y), 3)
    pygame.draw.line(DISPLAYSURF, GREEN, (axis_x-x_off, axis_y), (axis_x, axis_y), 3)


def terminate():
    pygame.quit()
    sys.exit()

def main():
    global FPS, FPSCLOCK, DISPLAYSURF, BASICFONT, A, T, w, start
    pygame.init()
    
    fpsClock = pygame.time.Clock()

    # set up the window
    DISPLAYSURF = pygame.display.set_mode((800, 600), 0, 32)
    pygame.display.set_caption('Sin Cos曲线联动')

    BASICFONT = pygame.font.Font('freesansbold.ttf', 20)
    ''' 画背景网格 '''
    drawBg()
    
    ''' 读取周期函数x与y的 映射 '''
    sinArr = MakeSinArr(w, T)
    Pause = False

    ''' 主循环 '''
    while True: # the main game loop
        
        if not Pause:
            # 画网格背景
            drawBg()

            # 画函数信息
            drawInfo(A,w,T)
        
            # 画正弦函数曲线
            DawSinCurveX(sinArr, start, pygame.Color('Red'))
            DawSinCurveY(sinArr, start+90, pygame.Color('Green'))
            '''
                动起来
                走1个周期, 结束之后可以按空格再次触发
                也可以将T 替换成 T * 1000 走1000个周期
            '''
            if start < (T * 4):
                start += 2
        
        for event in pygame.event.get():
            # 键盘事件
            if (event.type == KEYDOWN):
                # 回车键按下 且 不在运动中
                if event.key == K_RETURN and start >= T:
                    initEvn()
                # 方向键 上 下 或者 w s 按下： 调整振幅 A
                elif event.key == K_UP or event.key == K_w:
                    A += 10
                elif event.key == K_DOWN or event.key == K_s:
                    A -= 10
                    # 空格键按下暂停
                elif event.key == K_SPACE:
                    Pause = not Pause
                    # + 键调高帧速率
                elif event.key == K_EQUALS:
                    FPS = FPS + 2
                    # - 键调低帧速率
                elif event.key == K_MINUS:
                    if FPS > 2:
                        FPS = FPS - 2
                elif event.key == K_ESCAPE:
                    terminate()
            elif event.type == QUIT:
                terminate()

        pygame.display.update()
        fpsClock.tick(FPS)

if __name__ == '__main__':
    main()
